home *** CD-ROM | disk | FTP | other *** search
/ Internet Surfer: Getting Started / Internet Surfer - Getting Started (Wayzata Technology)(7231)(1995).bin / pc / mac / bonus / peter_le / macbinar / task.uni < prev   
Text File  |  1992-11-23  |  17KB  |  658 lines

  1. unit Tasks;
  2.  
  3. {        Task Manager -- Background processing support}
  4. {        version 2.2.1}
  5.  
  6. {    This software source package is Copyright ⌐ 1990-91 by Michael Hecht. All Rights}
  7. {    Reserved. It may be freely distributed in source or object code format; however,}
  8. {    the source code may not be sold for profit or charged for in any way. The source}
  9. {    code must be distributed as a package including all H files, sample code and}
  10. {    projects, and documentation.}
  11.  
  12. {    I welcome any comments or suggestions that will help me improve or extend the}
  13. {    functionality of the Task Manager. You can reach me at:}
  14.  
  15. {        Internet:        Michael_Hecht@mac.sas.com}
  16. {        AppleLink:        SAS.HECHT}
  17.  
  18. {    Happy Tasking!}
  19.  
  20. {    --Michael Hecht}
  21.  
  22. {        Pascal Conversion by Peter N Lewis <peter@cujo.curtin.edu.au>, Aug 1992 }
  23.  
  24. interface
  25.  
  26.     const
  27. { Globals }
  28.         CurStackBaseA = $908;
  29.         HeapEndA = $114;
  30.         WindowListA = $9D6;
  31.  
  32. { Define the following constant as true to include stack checking code }
  33. { Define the following constant as false to remove stack checking code }
  34.         TASK_DEBUG = false;
  35.  
  36. { Default options }
  37.         kDefaultWakeTime = 60;            { One second }
  38.  
  39.     type
  40.         longPtr = ^longInt;
  41.  
  42. { Task procedure }
  43.     type
  44.         TaskProcPtr = ProcPtr;
  45. { procedure TaskProc(taskRefCon:longInt) }
  46.  
  47.     procedure CallTaskProc (taskRefCon: longInt; p: TaskProcPtr);
  48.     inline
  49.         $205F, $4E90;
  50.  
  51.  
  52. { Routines }
  53.     function InitTasking: OSErr;
  54.     function TermTasking: OSErr;
  55.  
  56. { Creating tasks }
  57.     function NewTask (taskProc: TaskProcPtr; taskTermProc: TaskProcPtr; taskRefCon: univ longInt; var taskRefNum: integer): OSErr;
  58.     function DisposeTask (taskRefNum: integer): OSErr;
  59.  
  60. { Running tasks }
  61.     function RunTasks (wakeTime: longInt): OSErr;
  62.     function TaskYield: OSErr;
  63.  
  64. { Getting task info }
  65.     function CurrentTask: integer;
  66.     function CountTasks: integer;
  67.     function GetIndTask (index: integer): integer;
  68.  
  69. { Task reference constant }
  70.     function GetTaskRefCon (taskRefNum: integer): longInt;
  71.     function SetTaskRefCon (taskRefNum: integer; taskRefCon: longInt): OSErr;
  72.  
  73. implementation
  74.  
  75.     type
  76.         jmp_buf = record { preserve D2 as well! }
  77.                 d2, d3, d4, d5, d6, d7, a1, a2, a3, a4, a6, a7: longInt;
  78.             end;
  79.  
  80.     function setjmp (var regs: jmp_buf): integer;
  81.     inline
  82.         $205F, $7000, $43FA, $0006, $48D0, $DEFC, $3E80;
  83.  
  84.     procedure longjmp (var regs: jmp_buf; status: integer);
  85.     inline
  86.         $301F, $205F, $4CD8, $DEFC, $4ED1;
  87.  
  88.     function GetRegD2: Ptr;
  89.     inline
  90.         $2E82;
  91.  
  92.     procedure SetRegD2 (n: univ ptr);
  93.     inline
  94.         $241F;
  95.  
  96.     type
  97.         TaskEnvironmentRecord = record
  98.                 envRegisters: jmp_buf;
  99.                 envStack: handle;
  100.             end;
  101.         TaskEnvironmentPtr = ^TaskEnvironmentRecord;
  102.  
  103. { Register ordering within the jmp_buf }
  104.         regs = (d3, d4, d5, d6, d7,{}
  105.             a1, a2, a3, a4, a6, a7);
  106.  
  107.         TaskRecord = record
  108.                 taskProc: TaskProcPtr;
  109.                 taskTermProc: TaskProcPtr;
  110.                 taskRefCon: longInt;
  111.                 taskRefNum: integer;
  112.                 taskEnvironment: TaskEnvironmentRecord;
  113.                 taskFlags: longInt;
  114.             end;
  115.         TaskPtr = ^TaskRecord;
  116.  
  117. { Values for gTaskMgrFlags }
  118.     type
  119.         TaskMgrFlags = set of (useTempMem, tasksRunning);
  120.  
  121. { Return values from setjmp; negative values are OSErr's }
  122.     const
  123.         SJsaveEnvironment = 0;
  124.         SJtaskResume = 1;
  125.         SJtaskSuspend = 2;
  126.  
  127.     type
  128.         TaskList = record
  129.                 numTasks: integer;
  130.                 theTask: array[0..100] of TaskRecord;
  131.             end;
  132.         TaskListPtr = ^TaskList;
  133.         TaskListHandle = ^TaskListPtr;
  134.  
  135.     var
  136.         gTaskList: TaskListHandle;
  137.         gTaskAtHand: integer;
  138.         gCurrentTask: TaskRecord;
  139.         gNextTaskRefNum: integer;
  140.         gTaskMgrFlags: TaskMgrFlags;
  141.         gAppEnvironment: TaskEnvironmentRecord;
  142.         gTimeToStop: longInt;
  143.         CurStackBase: longInt;
  144.  
  145.     function SizeOfTaskList (n: integer): longInt;
  146.     begin
  147.         SizeOfTaskList := sizeof(integer) + n * sizeof(TaskRecord);
  148.     end;
  149.  
  150.     function InitTasking: OSErr;
  151.         var
  152.             err: OSErr;
  153.             response, tempMask: longInt;
  154.             CurStackBaseP: longPtr;
  155.     begin
  156.         CurStackBaseP := longPtr(CurStackBaseA);
  157.         CurStackBase := CurStackBaseP^;
  158.  
  159.         { Allocate the task list }
  160.         gTaskList := TaskListHandle(NewHandle(SizeOfTaskList(0)));
  161.         if gTaskList = nil then begin
  162.             err := MemError;
  163.         end
  164.         else begin
  165.  
  166.     { Initialize global data }
  167.             gTaskAtHand := -1;                { Run task 0 first }
  168.             gTaskList^^.numTasks := 0;
  169.             gNextTaskRefNum := 1;
  170.             gTaskMgrFlags := [];
  171.  
  172.     { Determine if temporary memory is available }
  173.             if Gestalt(gestaltOSAttr, response) = noErr then begin
  174.                 if BTST(response, gestaltTempMemSupport) and BTST(response, gestaltRealTempMemory) and BTST(response, gestaltTempMemTracked) then
  175.                     gTaskMgrFlags := gTaskMgrFlags + [useTempMem];
  176.             end;
  177.             err := noErr;
  178.         end;
  179.  
  180.         InitTasking := err;
  181.     end;
  182.  
  183.     function TermTasking: OSErr;
  184.         var
  185.             err: OSErr;
  186.             taskIndex: integer;
  187.     begin
  188.  
  189.     { Can't terminate from a task }
  190.         if tasksRunning in gTaskMgrFlags then begin
  191.             err := paramErr;
  192.         end
  193.         else begin
  194. {     *    Kill all tasks. We do this from back to front because it's more}
  195. {     *    efficient, for two reasons:}
  196. {     *}
  197. {     *    Ñ    Less memory gets moved when we shrink the task list.}
  198. {     *}
  199. {     *    Ñ    The taskIndexes are looked up much faster when starting at}
  200. {     *        the end of the list.}
  201.  
  202.             err := noErr;
  203.             for taskIndex := gTaskList^^.numTasks - 1 downto 0 do begin
  204.  
  205.                 err := DisposeTask(gTaskList^^.theTask[taskIndex].taskRefNum);
  206.                 if err <> noErr then
  207.                     leave;
  208.             end;
  209.  
  210. { Dispose of the task list }
  211.             DisposHandle(Handle(gTaskList));
  212.             gTaskList := nil;
  213.         end;
  214.         TermTasking := err;
  215.     end;
  216.  
  217.     function CountTasks: integer;
  218.     begin
  219.         CountTasks := gTaskList^^.numTasks;
  220.     end;
  221.  
  222.     function CurrentTask: integer;
  223.     begin
  224.         if tasksRunning in gTaskMgrFlags then
  225.             CurrentTask := gCurrentTask.taskRefNum
  226.         else
  227.             CurrentTask := 0;
  228.     end;
  229.  
  230.     function GetIndTask (index: integer): integer;
  231.     begin
  232.         if (index < 0) or (index >= gTaskList^^.numTasks) then
  233.             GetIndTask := 0
  234.         else
  235.             GetIndTask := gTaskList^^.theTask[index].taskRefNum;
  236.     end;
  237.  
  238.     function GetTaskIndex (taskRefNum: integer): integer;
  239.         var
  240.             taskIndex: integer;
  241.     begin
  242.  
  243. {     *    Since taskRefNums start at 1 and always increase, we can assume that}
  244. {     *    the taskIndex will always be less than the taskRefNum or the number of}
  245. {     *    tasks, which ever is smallest. This makes a good starting point in our}
  246. {     *    search for the task.}
  247.  
  248.  
  249.         taskIndex := gTaskList^^.numTasks;
  250.         if taskRefNum < taskIndex then
  251.             taskIndex := taskRefNum;
  252.         taskIndex := taskIndex - 1;
  253.  
  254.         while taskIndex >= 0 do begin
  255.             if gTaskList^^.theTask[taskIndex].taskRefNum = taskRefNum then
  256.                 leave;
  257.             taskIndex := taskIndex - 1;
  258.         end;
  259.  
  260.     { Note that a negative value will be returned if the task isn't found }
  261.         GetTaskIndex := taskIndex;
  262.     end;
  263.  
  264.     function GetTaskRefCon (taskRefNum: integer): longInt;
  265.         var
  266.             taskIndex: integer;
  267.     begin
  268.         taskIndex := GetTaskIndex(taskRefNum);
  269.         if taskIndex < 0 then begin
  270.             GetTaskRefCon := 0;
  271.         end
  272.         else begin
  273.             GetTaskRefCon := gTaskList^^.theTask[taskIndex].taskRefCon;
  274.         end;
  275.     end;
  276.  
  277.     function SetTaskRefCon (taskRefNum: integer; taskRefCon: longInt): OSErr;
  278.         var
  279.             taskIndex: integer;
  280.     begin
  281.         taskIndex := GetTaskIndex(taskRefNum);
  282.         if taskIndex < 0 then begin
  283.             SetTaskRefCon := paramErr;
  284.         end
  285.         else begin
  286.             gTaskList^^.theTask[taskIndex].taskRefCon := taskRefCon;
  287.             SetTaskRefCon := noErr;
  288.         end;
  289.     end;
  290.  
  291.     function AllocStack (var theEnvironment: TaskEnvironmentRecord; stackSize: Size): OSErr;
  292.         var
  293.             err: OSErr;
  294.     begin
  295.     { Has the stack already been allocated? }
  296.         if theEnvironment.envStack <> nil then begin
  297.  
  298.         { Try to reallocate it }
  299.             ReallocHandle(theEnvironment.envStack, stackSize);
  300.             if MemError = noErr then begin
  301.                 AllocStack := noErr;
  302.                 exit(AllocStack);
  303.             end;
  304.  
  305.         { Dispose of the stack and try allocating a whole new one }
  306.             DisposHandle(theEnvironment.envStack);
  307.         end;
  308.  
  309.     { Try temporary memory first }
  310.         if useTempMem in gTaskMgrFlags then begin
  311.  
  312.             theEnvironment.envStack := TempNewHandle(stackSize, err);
  313.             if err = noErr then begin
  314.                 AllocStack := noErr;
  315.                 exit(AllocStack);
  316.             end;
  317.         end;
  318.  
  319.     { If that didn't work, try allocating from the heap }
  320.         theEnvironment.envStack := NewHandle(stackSize);
  321.         AllocStack := MemError;
  322.     end;
  323.  
  324.     function SaveEnvironment (theEnvironment: TaskEnvironmentPtr): OSErr;
  325.         var
  326.             err: OSErr;
  327.             status: OSErr;
  328.             stackPtr: Ptr;
  329.             stackSize: Size;
  330.     begin
  331.  { preserve the environment ptr across the stack switch }
  332. {$PUSH}
  333. {$D-}
  334.         SetRegD2(theEnvironment);
  335.     { Save the registers }
  336.         status := setjmp(theEnvironment^.envRegisters);
  337.         theEnvironment := TaskEnvironmentPtr(GetRegD2);
  338. {$POP}
  339.         if status <> SJsaveEnvironment then begin
  340.  
  341.         { Restore the stack }
  342.             stackSize := GetHandleSize(theEnvironment^.envStack);
  343.             BlockMove(theEnvironment^.envStack^, ptr(CurStackBase - stackSize), stackSize);
  344.             HPurge(theEnvironment^.envStack);
  345.             err := status;
  346.         end
  347.         else begin
  348.  
  349.     { Allocate the stack }
  350.             stackPtr := Ptr(theEnvironment^.envRegisters.a7);
  351.             stackSize := CurStackBase - ord(stackPtr);
  352.             err := AllocStack(theEnvironment^, stackSize);
  353.             if err = noErr then begin
  354.     { Save the stack }
  355.                 BlockMove(stackPtr, theEnvironment^.envStack^, stackSize);
  356.             end;
  357.         end;
  358.         SaveEnvironment := err;
  359.     end;
  360.  
  361.     procedure RestoreEnvironment (theEnvironment: TaskEnvironmentPtr; status: OSErr);
  362.         var
  363.             HeapEndP: longPtr;
  364.             peek: WindowPeek;
  365.             msg, title: Str255;
  366.             WindowListP: ^windowPeek;
  367.     begin
  368.         HeapEndP := longPtr(HeapEndA);
  369.     { Can't let the stack cross into the heap! }
  370.         if theEnvironment^.envRegisters.a7 < HeapEndP^ then begin
  371.             if TASK_DEBUG then begin
  372.                 DebugStr('TaskMgr: Stack overflow');
  373.                 ExitToShell;
  374.             end
  375.             else begin
  376.                 SysError(28);
  377.             end;
  378.         end;
  379.  
  380.         if TASK_DEBUG then begin
  381.     { Look for windows in the stack and warn about them }
  382.             WindowListP := POINTER(WindowListA);
  383.             peek := WindowListP^;
  384.             while peek <> nil do begin
  385.                 if ord(peek) >= HeapEndP^ then begin
  386.                     GetWTitle(windowPtr(peek), title);
  387.                     DebugStr(concat('TaskMgr: Window in stack: ', title));
  388.                     ExitToShell;
  389.                 end;
  390.                 peek := peek^.nextWindow;
  391.             end;
  392.         end;
  393.  
  394.     { Restore the registers }
  395.         longjmp(theEnvironment^.envRegisters, status);
  396.     end;
  397.  
  398.     procedure StartNextTask;
  399.     begin
  400.     { Move to next task at hand }
  401.         gTaskAtHand := gTaskAtHand + 1;
  402.         if gTaskAtHand >= gTaskList^^.numTasks then
  403.             gTaskAtHand := 0;
  404.  
  405.     { Keep gCurrentTask up-to-date }
  406.         gCurrentTask := gTaskList^^.theTask[gTaskAtHand];
  407.  
  408.     { Start the next task }
  409.         RestoreEnvironment(@gCurrentTask.taskEnvironment, SJtaskResume);
  410.  
  411.     { This statement should never be hit }
  412.         DebugStr('TaskMgr/StartNextTask: returned from RestoreEnvironment!?!?');
  413.     end;
  414.  
  415.  
  416.     function RunTasks (wakeTime: longInt): OSErr;
  417.         var
  418.             status: OSErr;
  419.     begin
  420.  
  421.         if TASK_DEBUG then begin
  422.     { Called from task? }
  423.             if tasksRunning in gTaskMgrFlags then begin
  424.                 DebugStr('TaskMgr/RunTasks: Called from task');
  425.                 RunTasks := paramErr;
  426.                 exit(RunTasks);
  427.             end;
  428.         end;
  429.  
  430.     { Nothing to do if no tasks to run }
  431.         if gTaskList^^.numTasks = 0 then begin
  432.             RunTasks := noErr;
  433.             exit(RunTasks);
  434.         end;
  435.  
  436.     { Determine when to stop running tasks }
  437.         gTimeToStop := TickCount;
  438.         gTimeToStop := gTimeToStop + wakeTime;
  439.  
  440.     { Save application's state }
  441.         status := SaveEnvironment(@gAppEnvironment);
  442.         case status of
  443.             SJsaveEnvironment:  begin
  444.         { We just saved the application's environment; time to start next task }
  445.                 StartNextTask;
  446.         { StartNextTask never returns }
  447.             end;
  448.  
  449.             SJtaskSuspend:  begin
  450.         { Tasks have suspended execution; time to return to the application }
  451.                 status := noErr;
  452.             end;
  453.             otherwise begin
  454.         { Anything else is an OSErr code }
  455.  
  456.         { This case will be hit if SaveEnvironment couldn't }
  457.                 if TASK_DEBUG then
  458.                     DebugStr('TaskMgr/RunTasks: Can''t save environment');
  459.             end;
  460.         end;
  461.  
  462.         RunTasks := status;
  463.     end;
  464.  
  465.     function TaskYield: OSErr;
  466.         var
  467.             taskAtHand: integer;
  468.             status: OSErr;
  469.             timeToSuspend: Boolean;
  470.             theEvent: EventRecord;
  471.     begin
  472.  
  473.         if TASK_DEBUG then begin
  474.     { Called from application? }
  475.             if not (tasksRunning in gTaskMgrFlags) then begin
  476.                 DebugStr('TaskMgr/TaskYield: Called from application');
  477.                 TaskYield := paramErr;
  478.                 exit(TaskYield);
  479.             end;
  480.         end;
  481.  
  482. {     *    Determine if it's time to return to the application.}
  483. {     *}
  484. {     *    It's that time if the wake time has run out or if the application}
  485. {     *    received an event.}
  486.  
  487.         timeToSuspend := (TickCount >= gTimeToStop) or EventAvail(everyEvent, theEvent);
  488.  
  489. { if it's not time to suspend and I'm the only task, then I'll just keep running }
  490.         if not timeToSuspend and (gTaskList^^.numTasks = 1) then begin
  491.             TaskYield := noErr;
  492.             exit(TaskYield);
  493.         end;
  494.  
  495.     { Save the current task's environment }
  496.         status := SaveEnvironment(@gCurrentTask.taskEnvironment);
  497.  
  498.     { Return to the task }
  499.         if status <> SJsaveEnvironment then begin
  500.  
  501.             if status > noErr then begin
  502.                 status := noErr
  503.  
  504.         { A negative status is an OSErr }
  505.             end
  506.             else if TASK_DEBUG then begin
  507.                 DebugStr('TaskMgr/TaskYield: Can''t save environment');
  508.             end;
  509.  
  510.         { Tasks are running now }
  511.             gTaskMgrFlags := gTaskMgrFlags + [tasksRunning];
  512.  
  513.             TaskYield := status;
  514.             exit(TaskYield);
  515.         end;
  516.  
  517.     { Put the saved environment in the task list }
  518.         gTaskList^^.theTask[gTaskAtHand].taskEnvironment := gCurrentTask.taskEnvironment;
  519.  
  520.     { If it's time to return to the application, then do so }
  521.         if timeToSuspend then begin
  522.  
  523.         { Tasks no longer running }
  524.             gTaskMgrFlags := gTaskMgrFlags - [tasksRunning];
  525.  
  526.         { Return to the application }
  527.             RestoreEnvironment(@gAppEnvironment, SJtaskSuspend);
  528.         end;
  529.  
  530.     { Start the next task }
  531.         StartNextTask;
  532.     end;
  533.  
  534.     function DisposeTask (taskRefNum: integer): OSErr;
  535.         var
  536.             taskIndex: integer;
  537.             dyingTask: TaskRecord;
  538.             harakiri: Boolean;
  539.             dummy: longInt;
  540.     begin
  541.  
  542.         taskIndex := GetTaskIndex(taskRefNum);
  543.         if taskIndex < 0 then begin
  544.             DisposeTask := paramErr;
  545.             exit(DisposeTask);
  546.         end;
  547.  
  548.     { Are we deleting the current task? }
  549.         harakiri := (tasksRunning in gTaskMgrFlags) and (taskIndex = gTaskAtHand);
  550.  
  551.     { Point to the task record of the task we're disposing }
  552.         dyingTask := gTaskList^^.theTask[taskIndex];
  553.  
  554.     { If the task has a term proc, call it now }
  555.         if dyingTask.taskTermProc <> nil then
  556.             CallTaskProc(dyingTask.taskRefCon, dyingTask.taskTermProc);
  557.  
  558.     { We can dispose of its stack now }
  559.         DisposHandle(dyingTask.taskEnvironment.envStack);
  560.  
  561.     { Remove the task from the task list }
  562.         dummy := Munger(Handle(gTaskList), SizeOfTaskList(taskIndex), nil, sizeof(TaskRecord), @gTaskList, 0);
  563.  
  564.     { Fix up task at hand if necessary }
  565.         if gTaskAtHand >= taskIndex then begin
  566.             gTaskAtHand := gTaskAtHand - 1;
  567.             if gTaskAtHand < 0 then begin
  568.                 gTaskAtHand := gTaskList^^.numTasks;
  569.             end;
  570.         end;
  571.  
  572.     { One less task to keep track of }
  573.         gTaskList^^.numTasks := gTaskList^^.numTasks - 1;
  574.  
  575.     { Return to the application if we deleted ourselves }
  576.         if harakiri then begin
  577.  
  578.         { Tasks are no longer running (we will be returning to the application) }
  579.             gTaskMgrFlags := gTaskMgrFlags - [tasksRunning];
  580.             RestoreEnvironment(@gAppEnvironment, SJtaskSuspend);
  581.         end;
  582.  
  583.     { Not disposing of ourselves; return to the caller }
  584.         DisposeTask := noErr;
  585.     end;
  586.  
  587.     procedure TaskLife;
  588.         var
  589.             oe: OSErr;
  590.     begin
  591.     { TaskLife is the task's life cycle }
  592.  
  593.     { We are now running a task }
  594.         gTaskMgrFlags := gTaskMgrFlags + [tasksRunning];
  595.  
  596.     { Call the task procedure }
  597.         CallTaskProc(gCurrentTask.taskRefCon, gCurrentTask.taskProc);
  598.  
  599.     { Delete the task }
  600.         oe := DisposeTask(gCurrentTask.taskRefNum);
  601.     end;
  602.  
  603.     function NewTask (taskProc: TaskProcPtr; taskTermProc: TaskProcPtr; taskRefCon: univ longInt; var taskRefNum: integer): OSErr;
  604.         var
  605.             err: OSErr;
  606.             status: OSErr;
  607.             saveTask: TaskRecord;
  608.     begin
  609.  
  610.     { Make a backup copy of the current task }
  611.         saveTask := gCurrentTask;
  612.  
  613.     { Initialize the task record }
  614.         gCurrentTask.taskProc := taskProc;
  615.         gCurrentTask.taskTermProc := taskTermProc;
  616.         gCurrentTask.taskRefCon := taskRefCon;
  617.         gNextTaskRefNum := gNextTaskRefNum + 1;
  618.         gCurrentTask.taskRefNum := gNextTaskRefNum;
  619.         gCurrentTask.taskFlags := 0;
  620.         gCurrentTask.taskEnvironment.envStack := nil;
  621.  
  622.     { Give task refNum back to caller }
  623.         taskRefNum := gCurrentTask.taskRefNum;
  624.  
  625.     { Initialize the task's environment }
  626.         status := SaveEnvironment(@gCurrentTask.taskEnvironment);
  627.         if status < noErr then begin
  628.             err := status;
  629.         end
  630.         else begin
  631.  
  632.             if status > SJsaveEnvironment then begin
  633.                 TaskLife;
  634.         { Never to return╔ }
  635.             end;
  636.  
  637.     { Add task to task list }
  638.             err := PtrAndHand(@gCurrentTask, Handle(gTaskList), sizeof(TaskRecord));
  639.             if err <> noErr then begin
  640.  
  641.         { Dispose of the stack }
  642.                 DisposHandle(gCurrentTask.taskEnvironment.envStack);
  643.  
  644.         { Not enough memory to add it to the task list }
  645.             end
  646.             else begin
  647.                 gTaskList^^.numTasks := gTaskList^^.numTasks + 1;
  648.  
  649.     { All dressed up and nowhere to go }
  650.                 err := noErr;
  651.             end;
  652.         end;
  653.  
  654.         gCurrentTask := saveTask;
  655.         NewTask := err;
  656.     end;
  657.  
  658. end.